package com.schoolcardinfo.dao;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import com.schoolcardinfo.utils.LogUtil;



public class DBHelper {
	private Connection con=null;
	private PreparedStatement pstmt=null;
	private ResultSet rs=null;

	/**
	 * 数据库驱动的加载
	 */
	static{
		try {
			Class.forName(ReadPro.getInstance().getProperty("drivername"));
		} catch (ClassNotFoundException e) {
			//e.printStackTrace(); 我们将异常压制，这样一样如果调用者知道处理这个异常，也没办法
			LogUtil.log.error(e.toString()); //记录到日志文件中
			throw new RuntimeException(e); //抛出一个运行时异常，以便调用者处理可以处理这个异常
		}
	}

	/**
	 * 获取连接的方法
	 * @return
	 */
	public Connection getConnection(){
		try {
			//con=DriverManager.getConnection(ReadDBProperties.getInstance().getProperty("url"),ReadDBProperties.getInstance().getProperty("user"),ReadDBProperties.getInstance().getProperty("password"));
			con=DriverManager.getConnection(ReadPro.getInstance().getProperty("url"),ReadPro.getInstance());
		} catch (SQLException e) {
			//e.printStackTrace();
			LogUtil.log.error(e.toString());
			throw new RuntimeException(e);
		}
		return con;
	}
	
//	public Connection getConnection(){
//		try {
//			Context ct=new InitialContext();
//			DataSource ds=(DataSource)ct.lookup("java:comp/env/jdbc");
//			con=ds.getConnection();
//		} catch (NamingException e) {
//			LogUtil.log.error(e.toString());
//			throw new RuntimeException(e);
//		}catch(SQLException e){
//			LogUtil.log.error(e.toString());
//			throw new RuntimeException(e);
//		}
//		
//		return con;
//		
//	}
	
	/**
	 * 给占位符赋值的方法
	 * @param pstmt：要赋值的预编译
	 * @param params：对应的占位符的值的集合
	 * @throws SQLException 
	 * @throws SQLException 
	 */
	public void setValues(PreparedStatement pstmt,List<Object> params) throws SQLException{
		//判断params是否为空，如果不为空说明用户给定的sql语句中含有占位符 ?
		if(params!=null  && params.size()>0){
			Object obj=null;
			for(int i=0;i<params.size();i++){
				obj=params.get(i);
				if(obj!=null){
					try {
						if("[B".equals(obj.getClass().getName())){
							pstmt.setBytes(i+1,(byte[])obj );
						}else{
							pstmt.setString(i+1,String.valueOf( obj ));
						}
					} catch (SQLException e) {
						e.printStackTrace();
						LogUtil.log.error(e.toString());
					}
				}else{
					pstmt.setString(i+1,String.valueOf( obj ));
				}
			}
		}
	}

	/**
	 * 更行数据的方法
	 * @param sql：要执行的查询语句
	 * @param params：对应的sql语句中的问号的值
	 * @return：语句执行后，影响的数据的行数
	 */
	public int update(String sql,List<Object> params){
		con=this.getConnection(); //获取一个连接
		int result=0;

		try {
			pstmt=con.prepareStatement(sql);//预编译sql语句
			this.setValues(pstmt, params);
			//执行
			result=pstmt.executeUpdate();
		} catch (SQLException e) {
			//e.printStackTrace();
			LogUtil.log.error(e.toString());
			throw new RuntimeException(e);
		} finally{
			this.close(); //关闭打开的资源
		}
		return result;
	}

	/**
	 * 带事物的更新
	 * @param sqls：要执行的sql语句集合
	 * @param params：对应的每条sql语句的站位符的值
	 * @return：成功返回true
	 */
	public boolean update(List<String> sqls,List<List<Object>> params){
		con=this.getConnection();
		//java中是自动提交事务，所以我们必须先关闭自动提交
		try {
			con.setAutoCommit(false);
			//循环执行sql语句
			for(int i=0;i<sqls.size();i++){
				pstmt=con.prepareStatement(sqls.get(i)); //取出第i条sql语句
				this.setValues(pstmt, params.get(i)); //取出第i条sql语句对应的参数列表
				pstmt.executeUpdate();
			}
			//如果所有语句执行后都没有出错，则提交
			con.commit();
		} catch (SQLException e) {
			//如果执行过程中出错了，则回滚
			try {
				con.rollback();
			} catch (SQLException e1) {
				LogUtil.log.error(e.toString());
				throw new RuntimeException(e);
			}
			LogUtil.log.error(e.toString());
			throw new RuntimeException(e);
		} finally{
			try {
				con.setAutoCommit(true);
			} catch (SQLException e) {
				LogUtil.log.error(e.toString());
				throw new RuntimeException(e);
			}
			this.close();
		}
		return true;
	}


//	public boolean updates(List<String> sqls,List<List<Object>> params){
//		con=this.getConnection();
//		//java中是自动提交事务，所以我们必须先关闭自动提交
//		try {
//			con.setAutoCommit(false);
//			//循环执行sql语句
//			for(int i=0;i<sqls.size();i++){
//				pstmt=con.prepareStatement(sqls.get(i)); //取出第i条sql语句
//				this.setValues(pstmt, params.get(i)); //取出第i条sql语句对应的参数列表
//				pstmt.addBatch();
//			}
//			//如果所有语句执行后都没有出错，则提交
//			pstmt.executeBatch();
//			con.commit();
//		} catch (SQLException e) {
//			//如果执行过程中出错了，则回滚
//			try {
//				con.rollback();
//			} catch (SQLException e1) {
//				LogUtil.log.error(e.toString());
//				throw new RuntimeException(e);
//			}
//			LogUtil.log.error(e.toString());
//			throw new RuntimeException(e);
//		} finally{
//			try {
//				con.setAutoCommit(true);
//			} catch (SQLException e) {
//				LogUtil.log.error(e.toString());
//				throw new RuntimeException(e);
//			}
//			this.close();
//		}
//
//		return true;
//	}

	/**
	 * 查询数据的方法
	 * @param sql：要执行的查询语句
	 * @param params：对应的sql语句中的问号的值
	 * @throws IOException 
	 * @return：所有满足条件的数据的集合 Map<String,String> key为列名
	 */
	public List<Map<String,Object>> find(String sql,List<Object> params){
		List<Map<String,Object>> result=new ArrayList<Map<String,Object>>();
		BufferedInputStream bis=null;
		
		try {
			//获取连接
			con=this.getConnection();
			//预编译sql语句
			pstmt=con.prepareStatement(sql);

			//给占位符赋值
			this.setValues(pstmt, params);

			//执行语句并获取返回的结果集
			ResultSet rs=pstmt.executeQuery();

			//获取返回的结果集中列的信息
			ResultSetMetaData rsmd=rs.getMetaData();

			String[] cols=new String[rsmd.getColumnCount()];
			for(int i=0;i<cols.length;i++){ //循环获取每个列的列名存放到数组中
				cols[i]=rsmd.getColumnName(i+1);
			}

			Map<String,Object> map; //用来存放一条记录，以列名为key,对应列的值为value
			String colType; 
			Object obj;
			
			//因为封装在Map中，而map我们选用列名为key,对应列的值为value,则我们需要获取所有列的列名
			while(rs.next()){
				map=new HashMap<String,Object>();
				for(String col:cols){
					obj=rs.getObject( col );
					if(obj!=null){
						colType=obj.getClass().getName();
						if("oracle.sql.BLOB".equals(colType)){
							Blob bb=rs.getBlob(col);
							bis=new BufferedInputStream( bb.getBinaryStream() );
							byte[] bt;
							
							bt=new byte[ (int) bb.length() ];
							bis.read(bt);
							map.put(col,bt);
						}else{
							map.put(col,rs.getString(col));
						}
					}else{
						map.put(col,rs.getString(col));
					}
				}
				result.add(map);
			}
		} catch (SQLException e) {
			LogUtil.log.error(e.toString());
			throw new RuntimeException(e);
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			if(bis!=null){
				try {
					bis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			this.close();
		}
		return result;
	}

	/**
	 * 查询数据的方法
	 * @param sql：要执行的查询语句
	 * @param params：对应的sql语句中的问号的值
	 * @return：所有满足条件的数据的集合 Map<String,String> key为列名
	 */
	public List<List<String>> finds(String sql,List<Object> params){
		List<List<String>> results=new ArrayList<List<String>>(); //存放所有的记录
		List<String> result; //存放一条记录

		try {
			//获取连接
			con=this.getConnection();
			//预编译sql语句
			pstmt=con.prepareStatement(sql);

			//给占位符赋值
			this.setValues(pstmt, params);

			//执行语句并获取返回的结果集
			ResultSet rs=pstmt.executeQuery();

			//获取返回的结果集中列的信息
			ResultSetMetaData rsmd=rs.getMetaData();

			//因为封装在Map中，而map我们选用列名为key,对应列的值为value,则我们需要获取所有列的列名
			while(rs.next()){
				result=new ArrayList<String>();
				for(int i=0;i<rsmd.getColumnCount();i++){
					result.add(rs.getString(i+1));
				}
				results.add(result);
			}
		} catch (SQLException e) {
			LogUtil.log.error(e.toString());
			throw new RuntimeException(e);
		} finally{
			this.close();
		}
		return results;
	}

	/**
	 * 关闭资源的方法
	 */
	public void close(){
		if(rs!=null){
			try {
				rs.close();
			} catch (SQLException e) {
				//e.printStackTrace();
				LogUtil.log.error(e.toString());
				throw new RuntimeException(e);
			}
		}

		if(pstmt!=null){
			try {
				pstmt.close();
			} catch (SQLException e) {
				LogUtil.log.error(e.toString());
				throw new RuntimeException(e);
			}
		}

		if(con!=null){
			try {
				con.close();
			} catch (SQLException e) {
				//e.printStackTrace();
				LogUtil.log.error(e.toString());
				throw new RuntimeException(e);
			}
		}
	}
	
	/**
	 * 将数据库中的一条记录放射注入到一个类的对象中
	 * @param <T>:泛型
	 * @param sql:要执行的sql语句
	 * @param params:sql语句中对一个的占位符的值
	 * @param c:要注入的类的信息
	 * @return
	 */
	public <T> List<T> find(String sql,List<Object> params,Class<T> c){
		List<T> list=new ArrayList<T>();
		
		try {
			//获取连接
			con=this.getConnection();
			//预编译sql语句
			pstmt=con.prepareStatement(sql);
			//为占位符设值
			setValues(pstmt, params);
			//执行查询语句
			rs=pstmt.executeQuery();
			
			ResultSetMetaData rsmd=rs.getMetaData();
			//从元数据中取出所有列的列名，存到一个数组
			String[] colName=new String[rsmd.getColumnCount()];
			
			for(int i=0;i<colName.length;i++){
				colName[i]=rsmd.getColumnName(i+1);
			}
			
			//获取给定的类的所有方法
			Method[] methods=c.getMethods();
			
			T t;
			String methodName=null;//方法名
			String col=null;//列名
			String typeName=null;//类型名
			Object obj=null;
			while(rs.next()){
				//每循环一次就是一条记录，即一个对象
				t=c.newInstance();//获取该类的一个实例相当于new...
				
				//循环取出所有的列名
				for(String cl:colName){
					//然后在列名前面加一个set
					col="set"+cl;
					
					obj=rs.getObject(cl);//获取当前列中的数据的数据类型
					//循环所有的方法名，然后忽略大小写比较
					for(Method md:methods){
						methodName=md.getName();
						if(methodName.equalsIgnoreCase(col)){
							//获取当前循环的列的数据的类型
							if(obj!=null){
								typeName=obj.getClass().getName();
								if("java.lang.String".equals(typeName)){
									//md.invoke(t, (String)obj);
									md.invoke(t, rs.getString(cl));
								}else if("java.math.BigDecimal".equals(typeName)){
									try {
										md.invoke(t, rs.getDouble(cl));
									} catch (Exception e) {
										md.invoke(t, rs.getInt(cl));
									}
								}else if("oracle.sql.CLOB".equals(typeName)){
									md.invoke(t, rs.getString(cl));
								}else if("java.sql.Date".equals(typeName)){
									md.invoke(t, rs.getString(cl));
								}else if("java.lang.Integer".equals(typeName)){
									md.invoke(t, rs.getInt(cl));
								}else if("java.lang.Long".equals(typeName)){
									md.invoke(t, rs.getInt(cl));
								}else if("java.lang.Double".equals(typeName)){
									md.invoke(t, rs.getDouble(cl));
								}else{
									md.invoke(t, rs.getString(cl));
								}
							}
							break;
						}
					}
				}
				list.add(t);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			close();
		}
		return list;
	}

}

